const logEnable = false;
let isAndroid = false;
let ecBluetoothAdapterStateChangeCallback = () => { };
let ecBLEConnectionStateChangeCallback = () => { };
let ecDeviceId = '';
let ecGattServerUUID = '';
const ecGattServerUUIDOption1 = '0000FFF0-0000-1000-8000-00805F9B34FB';
const ecGattServerUUIDOption2 = 'FFF0';
let ecGattCharacteristicWriteUUID = '';
const ecGattCharacteristicWriteUUIDOption1 = '0000FFF2-0000-1000-8000-00805F9B34FB';
const ecGattCharacteristicWriteUUIDOption2 = 'FFF2';
const log = data => {
    if (logEnable) {
        console.log('[eciot]:' + JSON.stringify(data));
    }
};
const onBluetoothAdapterStateChange = cb => {
    ecBluetoothAdapterStateChangeCallback = cb;
};
// const getSetting = () => {
//   return new Promise(function (resolve, reject) {
//     jd.getSetting({
//       success(res) {
//         log(res);
//         if (res.authSetting && res.authSetting['scope.bluetooth']) {
//           resolve({
//             ok: true,
//             errCode: 0,
//             errMsg: ''
//           });
//         } else {
//           resolve({
//             ok: false,
//             errCode: 30001,
//             errMsg: 'getSetting fail'
//           });
//         }
//       },
//       fail(res) {
//         log(res);
//         resolve({
//           ok: false,
//           errCode: res.errCode ? res.errCode : 30000,
//           errMsg: res.errMsg ? res.errMsg : 'getSetting fail'
//         });
//       }
//     });
//   });
// };
// const authorize = () => {
//   return new Promise(function (resolve, reject) {
//     jd.authorize({
//       scope: 'scope.bluetooth',
//       success(res) {
//         log(res);
//         resolve({
//           ok: true,
//           errCode: 0,
//           errMsg: ''
//         });
//       },
//       fail(res) {
//         log(res);
//         // {"errMsg":"authorize:fail:auth deny"}
//         resolve({
//           ok: false,
//           errCode: 30000,
//           errMsg: res.errMsg
//         });
//       }
//     });
//   });
// };
const _openBluetoothAdapter = () => {
    return new Promise(function (resolve, reject) {
        jd.openBluetoothAdapter({
            success(res) {
                log(res);
                // {errno: 0, errMsg: "openBluetoothAdapter:ok"}
                resolve({
                    ok: true,
                    errCode: 0,
                    errMsg: ''
                });
            },
            fail(res) {
                log(res);
                resolve({
                    ok: false,
                    errCode: res.errCode ? res.errCode : 30000,
                    errMsg: res.errMsg
                });
            }
        });
    });
};
const openBluetoothAdapter = async () => {
    // await _openBluetoothAdapter()
    const systemInfo = jd.getSystemInfoSync();
    log(systemInfo);
    if (systemInfo.platform.toLowerCase() === 'android') {
        isAndroid = true;
    }
    //   if (!systemInfo.bluetoothEnabled) {
    //     ecBluetoothAdapterStateChangeCallback({
    //       ok: false,
    //       errCode: 30001,
    //       errMsg: '请打开系统蓝牙开关'
    //     });
    //     return;
    //   }
    //   if (isAndroid && !systemInfo.locationEnabled) {
    //     ecBluetoothAdapterStateChangeCallback({
    //       ok: false,
    //       errCode: 30002,
    //       errMsg: '请打开系统定位开关'
    //     });
    //     return;
    //   }
    //   if (isAndroid && !systemInfo.locationAuthorized) {
    //     ecBluetoothAdapterStateChangeCallback({
    //       ok: false,
    //       errCode: 30003,
    //       errMsg: '请打开微信定位权限，允许微信使用您的位置信息'
    //     });
    //     return;
    //   }
    //   const setting = await getSetting(); //小程序蓝牙权限
    //   if (!setting.ok) {
    //     const authRes = await authorize();
    //     if (!authRes.ok) {
    //       ecBluetoothAdapterStateChangeCallback({
    //         ok: false,
    //         errCode: 30004,
    //         errMsg: '请打开小程序蓝牙开关，点击右上角三个点，然后点击设置'
    //       });
    //       return;
    //     }
    //   }
    jd.offBluetoothAdapterStateChange();
    jd.onBluetoothAdapterStateChange(res => {
        log(res); // {available: true, discovering: true}
        if (!res.available) {
            ecBluetoothAdapterStateChangeCallback({
                ok: false,
                errCode: 30005,
                errMsg: '蓝牙适配器不可用'
            });
        }
    });
    const openRes = await _openBluetoothAdapter();
    ecBluetoothAdapterStateChangeCallback(openRes);
};
const onBluetoothDeviceFound = cb => {
    jd.offBluetoothDeviceFound();
    jd.onBluetoothDeviceFound(res => {
        // log(res);
        const device = res.devices[0];
        const name = device.name ? device.name : device.localName;
        if (!name) {
            return;
        }
        let id = device.deviceId;
        let rssi = device.RSSI;
        cb({
            id,
            name,
            rssi
        });
    });
};
const startBluetoothDevicesDiscovery = () => {
    jd.startBluetoothDevicesDiscovery({
        //services: [ecServerId],
        allowDuplicatesKey: true,
        powerLevel: 'high',
        complete(res) {
            log(res);
        }
    });
};
const stopBluetoothDevicesDiscovery = () => {
    jd.stopBluetoothDevicesDiscovery({
        complete(res) {
            // {errno: 0, errMsg: "stopBluetoothDevicesDiscovery:ok", isDiscovering: false}
            log(res);
        }
    });
};
const onBLEConnectionStateChange = cb => {
    ecBLEConnectionStateChangeCallback = cb;
};
const _createBLEConnection = () => {
    return new Promise(function (resolve, reject) {
        jd.createBLEConnection({
            deviceId: ecDeviceId,
            success(res) {
                log(res);
                // {"errno":0,"errCode":0,"errMsg":"createBLEConnection:ok"}
                resolve({
                    ok: true,
                    errCode: 0,
                    errMsg: ''
                });
            },
            fail(res) {
                log(res);
                // {"errno":1001,"errMsg":"createBLEConnection:fail parameter error: parameter.deviceId should be String instead of Undefined;"}
                resolve({
                    ok: false,
                    errCode: res.errCode ? res.errCode : res.errno,
                    errMsg: res.errMsg
                });
            }
        });
        if (!isAndroid) {
            setTimeout(() => {
                jd.createBLEConnection({
                    deviceId: ecDeviceId,
                    success(res) {
                        log(res);
                        // {"errno":0,"errCode":0,"errMsg":"createBLEConnection:ok"}
                        resolve({
                            ok: true,
                            errCode: 0,
                            errMsg: ''
                        });
                    },
                    fail(res) {
                        log(res);
                        // {"errno":1001,"errMsg":"createBLEConnection:fail parameter error: parameter.deviceId should be String instead of Undefined;"}
                        resolve({
                            ok: false,
                            errCode: res.errCode ? res.errCode : res.errno,
                            errMsg: res.errMsg
                        });
                    }
                });
            }, 1000)
        }
    });
};
const getBLEDeviceServices = () => {
    return new Promise(function (resolve, reject) {
        jd.getBLEDeviceServices({
            deviceId: ecDeviceId,
            success(res) {
                log(res);
                //{"services":[{"uuid":"0000FFF0-0000-1000-8000-00805F9B34FB","isPrimary":true}],"errCode":0,"errno":0,"errMsg":"getBLEDeviceServices:ok"}
                // {"errno":0,"deviceId":"7C7E20F2-CB75-6DA8-F8DF-FFF702B0D63F","services":[{"isPrimary":true,"uuid":"0000FFF0-0000-1000-8000-00805F9B34FB"}],"errMsg":"getBLEDeviceServices:ok","errCode":0}
                resolve({
                    ok: true,
                    errCode: 0,
                    errMsg: '',
                    services: res.services
                });
            },
            fail(res) {
                log(res);
                resolve({
                    ok: false,
                    errCode: res.errCode,
                    errMsg: res.errMsg
                });
            }
        });
    });
};
const getBLEDeviceCharacteristics = serviceId => {
    return new Promise(function (resolve, reject) {
        jd.getBLEDeviceCharacteristics({
            deviceId: ecDeviceId,
            serviceId,
            success(res) {
                log(res);
                // {"characteristics":[{"uuid":"0000FFF2-0000-1000-8000-00805F9B34FB","handle":3,"properties":{"read":false,"write":true,"notify":false,"indicate":false,"writeNoResponse":true,"writeDefault":true}},{"uuid":"0000FFF1-0000-1000-8000-00805F9B34FB","handle":5,"properties":{"read":true,"write":true,"notify":true,"indicate":false,"writeNoResponse":true,"writeDefault":true}}],"errCode":0,"errno":0,"errMsg":"getBLEDeviceCharacteristics:ok"}
                // {"characteristics":[{"properties":{"writeDefault":true,"notify":false,"write":true,"indicate":false,"read":false,"writeNoResponse":true},"uuid":"0000FFF2-0000-1000-8000-00805F9B34FB"},{"properties":{"writeDefault":true,"notify":true,"write":true,"indicate":false,"read":true,"writeNoResponse":true},"uuid":"0000FFF1-0000-1000-8000-00805F9B34FB"}],"deviceId":"7C7E20F2-CB75-6DA8-F8DF-FFF702B0D63F","serviceId":"0000FFF0-0000-1000-8000-00805F9B34FB","errno":0,"errMsg":"getBLEDeviceCharacteristics:ok","errCode":0}
                resolve({
                    ok: true,
                    errCode: 0,
                    errMsg: '',
                    characteristics: res.characteristics
                });
            },
            fail(res) {
                log(res);
                resolve({
                    ok: false,
                    errCode: res.errCode,
                    errMsg: res.errMsg
                });
            }
        });
    });
};
const notifyBLECharacteristicValueChange = (serviceId, characteristicId) => {
    return new Promise(function (resolve, reject) {
        jd.notifyBLECharacteristicValueChange({
            state: true,
            deviceId: ecDeviceId,
            serviceId,
            characteristicId,
            success(res) {
                log(res);
                // {"errCode":0,"errno":0,"errMsg":"notifyBLECharacteristicValueChange:ok"}
                resolve({
                    ok: true,
                    errCode: 0,
                    errMsg: ''
                });
            },
            fail(res) {
                log(res);
                resolve({
                    ok: false,
                    errCode: res.errCode,
                    errMsg: res.errMsg
                });
            }
        });
    });
};
const setBLEMTU = mtu => {
    return new Promise(function (resolve, reject) {
        jd.setBLEMTU({
            deviceId: ecDeviceId,
            mtu,
            success(res) {
                log(res);
                // {"errMsg":"setBLEMTU:ok","errno":0,"errCode":0,"mtu":50}
                resolve({
                    ok: true,
                    errCode: 0,
                    errMsg: ''
                });
            },
            fail(res) {
                log(res);
                // {"errCode":-1,"errno":1500104,"errMsg":"setBLEMTU:fail:internal error"}
                resolve({
                    ok: false,
                    errCode: res.errCode,
                    errMsg: res.errMsg
                });
            }
        });
    });
};
//和设备建立连接
const createBLEConnection = async id => {
    ecDeviceId = id;
    jd.offBLEConnectionStateChange();
    jd.onBLEConnectionStateChange(async res => {
        log(res);
        // {"deviceId":"EC:22:05:13:78:49","connected":true}
        if (res.connected) {
            const servicesResult = await getBLEDeviceServices();
            if (!servicesResult.ok) {
                ecBLEConnectionStateChangeCallback(servicesResult);
                closeBLEConnection();
                return;
            }
            for (const service of servicesResult.services) {
                if (service.uuid.toUpperCase() === ecGattServerUUIDOption1 || service.uuid.toUpperCase() === ecGattServerUUIDOption2) {
                    ecGattServerUUID = service.uuid;
                }
                const characteristicsResult = await getBLEDeviceCharacteristics(service.uuid);
                if (!characteristicsResult.ok) {
                    ecBLEConnectionStateChangeCallback(characteristicsResult);
                    closeBLEConnection();
                    return;
                }
                for (const characteristic of characteristicsResult.characteristics) {
                    if (characteristic.properties && characteristic.properties.notify) {
                        const notifyResult = await notifyBLECharacteristicValueChange(service.uuid, characteristic.uuid);
                        if (!notifyResult.ok) {
                            ecBLEConnectionStateChangeCallback({
                                ok: false,
                                errCode: 30000,
                                errMsg: 'notify error'
                            });
                            closeBLEConnection();
                            return;
                        }
                    }
                    if (characteristic.uuid.toUpperCase() === ecGattCharacteristicWriteUUIDOption1 || characteristic.uuid.toUpperCase() === ecGattCharacteristicWriteUUIDOption2) {
                        ecGattCharacteristicWriteUUID = characteristic.uuid;
                    }
                }
            }
            if (isAndroid) {
                await setBLEMTU(247);
            }
            ecBLEConnectionStateChangeCallback({
                ok: true,
                errCode: 0,
                errMsg: ''
            });
        } else {
            ecBLEConnectionStateChangeCallback({
                ok: false,
                errCode: 0,
                errMsg: 'disconnect'
            });
        }
    });
    const res = await _createBLEConnection();
    if (!res.ok) {
        ecBLEConnectionStateChangeCallback(res);
    }
};
//关闭当前连接
const closeBLEConnection = () => {
    jd.closeBLEConnection({
        deviceId: ecDeviceId,
        complete(res) {
            log(res);
        }
    });
};
const onBLECharacteristicValueChange = cb => {
    jd.offBLECharacteristicValueChange();
    jd.onBLECharacteristicValueChange(res => {
        log(res);
        let x = new Uint8Array(res.value);
        log(x);
        let str = utf8BytesToStr(x);
        let strHex = '';
        for (let i = 0; i < x.length; i++) {
            strHex = strHex + x[i].toString(16).padStart(2, '0').toUpperCase();
        }
        log(str);
        log(strHex);
        cb(str, strHex);
    });
};
const _writeBLECharacteristicValue = buffer => {
    return new Promise(function (resolve, reject) {
        jd.writeBLECharacteristicValue({
            deviceId: ecDeviceId,
            serviceId: ecGattServerUUID,
            characteristicId: ecGattCharacteristicWriteUUID,
            value: buffer,
            writeType: 'writeNoResponse',
            success(res) {
                log(res);
                // {"errno":0,"errCode":0,"errMsg":"writeBLECharacteristicValue:ok"}
                resolve({
                    ok: true,
                    errCode: 0,
                    errMsg: ''
                });
            },
            fail(res) {
                log(res);
                resolve({
                    ok: false,
                    errCode: res.errCode,
                    errMsg: res.errMsg
                });
            }
        });
    });
};
const writeBLECharacteristicValue = async (str, isHex) => {
    if (str.length === 0) return {
        ok: false,
        errCode: 30000,
        errMsg: 'data is null'
    };
    let buffer;
    if (isHex) {
        buffer = new ArrayBuffer(str.length / 2);
        let x = new Uint8Array(buffer);
        for (let i = 0; i < x.length; i++) {
            x[i] = parseInt(str.substr(2 * i, 2), 16);
        }
    } else {
        buffer = new Uint8Array(strToUtf8Bytes(str)).buffer;
    }
    return await _writeBLECharacteristicValue(buffer);
};
const utf8BytesToStr = utf8Bytes => {
    let unicodeStr = '';
    for (let pos = 0; pos < utf8Bytes.length;) {
        let flag = utf8Bytes[pos];
        let unicode = 0;
        if (flag >>> 7 === 0) {
            unicodeStr += String.fromCharCode(utf8Bytes[pos]);
            pos += 1;
        }
        // else if ((flag & 0xFC) === 0xFC) {
        //     unicode = (utf8Bytes[pos] & 0x3) << 30
        //     unicode |= (utf8Bytes[pos + 1] & 0x3F) << 24
        //     unicode |= (utf8Bytes[pos + 2] & 0x3F) << 18
        //     unicode |= (utf8Bytes[pos + 3] & 0x3F) << 12
        //     unicode |= (utf8Bytes[pos + 4] & 0x3F) << 6
        //     unicode |= (utf8Bytes[pos + 5] & 0x3F)
        //     unicodeStr += String.fromCharCode(unicode)
        //     pos += 6
        // }
        // else if ((flag & 0xF8) === 0xF8) {
        //     unicode = (utf8Bytes[pos] & 0x7) << 24
        //     unicode |= (utf8Bytes[pos + 1] & 0x3F) << 18
        //     unicode |= (utf8Bytes[pos + 2] & 0x3F) << 12
        //     unicode |= (utf8Bytes[pos + 3] & 0x3F) << 6
        //     unicode |= (utf8Bytes[pos + 4] & 0x3F)
        //     unicodeStr += String.fromCharCode(unicode)
        //     pos += 5
        // }
        else if ((flag & 0xf0) === 0xf0) {
            unicode = (utf8Bytes[pos] & 0xf) << 18;
            unicode |= (utf8Bytes[pos + 1] & 0x3f) << 12;
            unicode |= (utf8Bytes[pos + 2] & 0x3f) << 6;
            unicode |= utf8Bytes[pos + 3] & 0x3f;
            unicodeStr += String.fromCharCode(unicode);
            pos += 4;
        } else if ((flag & 0xe0) === 0xe0) {
            unicode = (utf8Bytes[pos] & 0x1f) << 12;
            unicode |= (utf8Bytes[pos + 1] & 0x3f) << 6;
            unicode |= utf8Bytes[pos + 2] & 0x3f;
            unicodeStr += String.fromCharCode(unicode);
            pos += 3;
        } else if ((flag & 0xc0) === 0xc0) {
            //110
            unicode = (utf8Bytes[pos] & 0x3f) << 6;
            unicode |= utf8Bytes[pos + 1] & 0x3f;
            unicodeStr += String.fromCharCode(unicode);
            pos += 2;
        } else {
            unicodeStr += String.fromCharCode(utf8Bytes[pos]);
            pos += 1;
        }
    }
    return unicodeStr;
};
const strToUtf8Bytes = str => {
    let bytes = [];
    for (let i = 0; i < str.length; ++i) {
        let code = str.charCodeAt(i);
        if (code >= 0x10000 && code <= 0x10ffff) {
            bytes.push(code >> 18 | 0xf0); // 第一个字节
            bytes.push(code >> 12 & 0x3f | 0x80);
            bytes.push(code >> 6 & 0x3f | 0x80);
            bytes.push(code & 0x3f | 0x80);
        } else if (code >= 0x800 && code <= 0xffff) {
            bytes.push(code >> 12 | 0xe0);
            bytes.push(code >> 6 & 0x3f | 0x80);
            bytes.push(code & 0x3f | 0x80);
        } else if (code >= 0x80 && code <= 0x7ff) {
            bytes.push(code >> 6 | 0xc0);
            bytes.push(code & 0x3f | 0x80);
        } else {
            bytes.push(code);
        }
    }
    return bytes;
};
module.exports = {
    onBluetoothAdapterStateChange,
    openBluetoothAdapter,

    onBluetoothDeviceFound,
    startBluetoothDevicesDiscovery,
    stopBluetoothDevicesDiscovery,

    onBLEConnectionStateChange,
    createBLEConnection,
    closeBLEConnection,

    onBLECharacteristicValueChange,
    writeBLECharacteristicValue
};